//! 快照消息
//!
//!

use crate::{
    bean::message::base_message::BaseMessage,
    bean::message::{message_type::MessageType, snapshot_point::SnapshotPoint},
    core::{constant, event_body::BigInt},
    error::{error_msg, Error},
    mnemonic,
    utils::sign,
};
use base64::Engine;
use crypto::{
    digest::Digest,
    sha3::{Sha3, Sha3Mode},
};
use hdwallet::secp256k1::Secp256k1;
use serde::{Deserialize, Serialize};
use std::ops::{Deref, DerefMut};

#[derive(Debug, Clone, Default)]
pub struct SnapshotMessage {
    base_message: BaseMessage,
    snap_version: BigInt,          // 本快照版本号
    pre_hash: String,              // 上一个快照的hash
    hash: String,                  // 本快照的hash
    snapshot_point: SnapshotPoint, // 本快照对应的快照点
    e_hash: String,                // 所属打包Event的hash值
}

impl SnapshotMessage {
    pub fn new() -> Self {
        Self::default()
    }

    /// words: 助记词
    /// passphrase: 生成助记词的随机字符串
    /// from_address: 发送地址
    /// snap_version: 快照版本
    /// pre_hash: 上一次快照hash
    /// snapshot_point: 当前快照点
    pub fn new_(
        words: &str,
        passphrase: &str,
        from_address: &str,
        snap_version: &BigInt,
        pre_hash: &str,
        snapshot_point: &SnapshotPoint,
    ) -> Result<Self, Error> {
        if words.is_empty() || words.split(" ").count() != constant::MNEMONIC_WORD_SIZE {
            return Err(error_msg("words is illegal"));
        }
        if from_address.is_empty() {
            return Err(error_msg("from_address is illegal"));
        }

        if snap_version.deref() <= &num_bigint::BigInt::from(0) {
            return Err(error_msg("snap_version is illegal"));
        }

        let mut new_one = Self::new();
        new_one.set_msg_type(MessageType::SnapShot);
        new_one.set_from_address(from_address);
        new_one.set_snap_version(snap_version.clone());
        new_one.set_pre_hash(pre_hash);
        new_one.set_snapshot_point(snapshot_point);
        new_one.set_timestamp(chrono::Local::now().timestamp());
        new_one.sign_message(words, passphrase)?;
        Ok(new_one)
    }

    fn sign_message(&mut self, words: &str, passphrase: &str) -> Result<(), Error> {
        let mn = mnemonic::Mnemonic::new(words, passphrase)?;
        let keys = mn.keypair(44, 0, 0, 0, 0.into())?;
        let pri_key = keys.secret_key();
        let pub_key = pri_key.public_key(&Secp256k1::new());
        self.set_pub_key(&base64::engine::general_purpose::STANDARD.encode(pub_key.serialize()));
        let signature = sign::sign(&self.get_message()?, &pri_key)?;
        self.set_signature(&signature);
        let mut sha = Sha3::new(Sha3Mode::Sha3_256);
        let mut hash_bytes = [0u8; 32];
        sha.input(&base64::engine::general_purpose::STANDARD.decode(&signature[4..])?);
        sha.result(&mut hash_bytes);
        self.hash = base64::engine::general_purpose::STANDARD.encode(&hash_bytes);
        Ok(())
    }

    fn get_message(&self) -> Result<String, Error> {
        let mut inner = Inner::default();
        if self.from_address().is_empty() {
            return Err(error_msg("from_address is illegal"));
        }
        inner.from_address = self.from_address().to_string();
        inner.pre_hash = self.pre_hash().to_string();
        if self.pub_key().is_empty() {
            return Err(error_msg("pubkey is illegal"));
        }
        inner.pubkey = self.pub_key().to_string();
        inner.snapshot_point = self.snapshot_point().clone();
        if self.snap_version().deref() <= &num_bigint::BigInt::from(0) {
            return Err(error_msg("snapshot_version is illegal"));
        }
        inner.snap_version = self.snap_version().clone();
        inner.timestamp = self.timestamp();
        if self.msg_type() != MessageType::SnapShot {
            return Err(error_msg("type is illegal"));
        }
        inner.r#type = self.msg_type();
        if self.signature().is_empty() {
            inner.signature = None;
            Ok(serde_json::to_string(&inner)?)
        } else {
            inner.signature = Some(self.signature().to_string());
            let msg = MessageJson { message: inner };
            Ok(serde_json::to_string(&msg)?)
        }
    }

    pub fn snap_version(&self) -> &BigInt {
        &self.snap_version
    }

    pub fn pre_hash(&self) -> &str {
        &self.pre_hash
    }

    pub fn hash(&self) -> &str {
        &self.hash
    }

    pub fn snapshot_point(&self) -> &SnapshotPoint {
        &self.snapshot_point
    }

    pub fn e_hash(&self) -> &str {
        &self.e_hash
    }

    pub fn set_snap_version(&mut self, vers: BigInt) {
        self.snap_version = vers;
    }

    pub fn set_pre_hash(&mut self, pre_hash: &str) {
        self.pre_hash = pre_hash.to_string();
    }

    pub fn set_hash(&mut self, hash: &str) {
        self.hash = hash.to_string();
    }

    pub fn set_snapshot_point(&mut self, snapshot_point: &SnapshotPoint) {
        self.snapshot_point = snapshot_point.clone();
    }

    pub fn set_e_hash(&mut self, e_hash: &str) {
        self.e_hash = e_hash.to_string();
    }
}

impl Deref for SnapshotMessage {
    type Target = BaseMessage;

    fn deref(&self) -> &Self::Target {
        &self.base_message
    }
}

impl DerefMut for SnapshotMessage {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.base_message
    }
}

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct MessageJson {
    message: Inner,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Inner {
    from_address: String,
    pre_hash: String,
    pubkey: String,
    snapshot_point: SnapshotPoint,
    snap_version: BigInt,
    timestamp: i64,
    r#type: MessageType,
    #[serde(skip_serializing_if = "Option::is_none")]
    signature: Option<String>,
}

#[cfg(test)]
mod tests {
    use crate::{
        bean::message::{snapshot_message::SnapshotMessage, snapshot_point::SnapshotPoint},
        mnemonic::Mnemonic,
    };

    #[test]
    fn test_snapshot_message() {
        let mnemonic = Mnemonic::new("", "hwllo").unwrap();
        let words = mnemonic.mnemonic();
        let mut s = SnapshotMessage::new_(
            words,
            "hwllo",
            "192.168.0.1",
            &num_bigint::BigInt::from(10).into(),
            "nihao",
            &SnapshotPoint::default(),
        )
        .unwrap();
        println!("sig: {}, hash: {}", s.signature(), s.hash());
        println!("msg: {}", s.get_message().unwrap());
        println!("s: {:?}", s);
    }
}
